from win32com.client import Dispatch, GetObject
from common.common import Common
import poplib
import time
import sys

class NotImplemented(Exception):
    pass

class Exchange2003DispatcherError(Exception):
    pass

class MailboxError(Exception):
    pass

class MailboxDatabaseError(Exception):
    pass										

class Exchange2003Util(object):
    def __init__(self):
        self.common = Common()
        self.log = self.common.log     

    def connect_to_mailbox_pop3(self, user, password, server_name = 'localhost', loops = 20):
        mail_number = None
        if loops <= 0:
            loops = 1 
        for i in range(loops):
            try:
                server = poplib.POP3(server_name, 110)
                server.user(user)
                server.pass_(password)
                mail_number = server.list()[1]
                server.quit()
            except:
                del server
                self.log.debug(str(sys.exc_info()[1]))                
                time.sleep(60)                
            else:
                break
        if mail_number is None:
            raise MailboxError('Possible problem in checking mailbox connectivity with user: {0}'.format(user))        
        
    def _get_ad_object(self, dn):
        return GetObject('LDAP://{0}'.format(dn))   
    
    def _is_recovery_object(self, dn):
        ad_obj = self._get_ad_object(dn)
        try:
            ad_obj.Get('msExchRestore')
        except:
            return False
        else:
            self.log.debug('object: {0} is has recovery flag'.format(dn))
            return True

    def _set_recovery_flag(self, dn, action = True):
        ad_obj = self._get_ad_object(dn)   
        ad_obj.Put('msExchRestore', ['FALSE', 'TRUE'][action])     
        ad_obj.SetInfo()    

class Exchange2003Dispatcher(Exchange2003Util):    

    def __init__(self, obj, name):
        self.dispatched_obj = Dispatch(obj)
        self.interface = self.dispatched_obj.GetInterface('IDataSource')
        self.interface.Open(name)
        Exchange2003Util.__init__(self)        

    def __getattr__(self, name):                
        return self.dispatched_obj.__getattr__(name)

    def commit(self):
        self.interface.Save()

class ExchangeServer(Exchange2003Dispatcher):
    def __init__(self, obj, server_name):     
        Exchange2003Dispatcher.__init__(self, obj, server_name)        

    def get_storage_groups(self):
        for sg_dn in self.dispatched_obj.StorageGroups:            
            yield StorageGroup('CDOEXM.StorageGroup', sg_dn)

    def get_storage_group(self, name):        
        for sg_dn in self.dispatched_obj.StorageGroups:
            if name in sg_dn:
                if self._is_recovery_object(sg_dn):
                    self._set_recovery_flag(sg_dn, False)
                self.log.debug('found storage group: {0}, distinguish name: {1}'.format(name, sg_dn))
                return StorageGroup('CDOEXM.StorageGroup', sg_dn)

class StorageGroup(Exchange2003Dispatcher):

    def __init__(self, obj, sg_dn):
        Exchange2003Dispatcher.__init__(self, obj, sg_dn)        
        self.sg_dn = sg_dn

    def get_guid(self):
        raise NotImplemented('method is not implemented for exchange 2003')

    def set_circular_logging(self, action = True):
        self.log.debug('setting circular logging: {0}'.format(action))            
        self.dispatched_obj.CircularLogging = action
        self.commit()
        
    def get_mailbox_database(self, name):
        return self._get_object(self.dispatched_obj.MailboxStoreDBs, 'CDOEXM.MailboxStoreDB', name)    
    
    def get_public_folder(self, name):
        return self._get_object(self.dispatched_obj.PublicStoreDBs, 'CDOEXM.PublicStoreDB', name)        
            
    def get_public_folders(self):
        return self._get_objects_list(self.dispatched_obj.PublicStoreDBs, 'CDOEXM.PublicStoreDB')
    
    def get_mailbox_databases(self):
        return self._get_objects_list(self.dispatched_obj.MailboxStoreDBs, 'CDOEXM.MailboxStoreDB')    
            
    def _get_objects_list(self, dispatched_object, wmi_string):
        for mdb_dn in dispatched_object:
            yield Database(wmi_string, mdb_dn)       
            
    def _get_object(self, dispatched_object, wmi_string, name):
        for mdb_dn in dispatched_object:
            if name in mdb_dn:
                if self._is_recovery_object(mdb_dn):
                    self._set_recovery_flag(mdb_dn, False)
                self.log.debug('found mailbox db: {0}, distinguish name: {1}'.format(name, mdb_dn))
                return Database(wmi_string, mdb_dn)                                  
                                    
class Database(Exchange2003Dispatcher):

    def __init__(self, obj, mdb_dn):
        Exchange2003Dispatcher.__init__(self, obj, mdb_dn)
        self.mdb_dn = mdb_dn
        self.ad_obj = GetObject('LDAP://{0}'.format(mdb_dn))
        self.wmi = None
        self.is_public_store = hasattr(obj, 'FolderTree')

    def set_db_overwritten_by_restore(self, action = True):
        self.log.debug('set overwritten by restore: {0}'.format(action))
        self.ad_obj.Put('msExchPatchMDB', ['FALSE', 'TRUE'][action])
        self.ad_obj.SetInfo()

    def is_db_overwritten_by_restore(self):
        self.log.debug('check if overwritten by restore set for : {0}'.format(self.mdb_dn))
        return self.ad_obj.Get('msExchPatchMDB')

    def get_mailboxes(self):
        if self.is_public_store:
            raise MailboxDatabaseError('object is not an mailbox database')
        self.__open_exchange_wmi()
        mailboxes_list = self.wmi.ExecQuery('Select * from Exchange_Mailbox')
        for mailbox_obj in mailboxes_list:
            if 'System' not in mailbox_obj.MailboxDisplayName and 'SMTP' not in mailbox_obj.MailboxDisplayName\
            and mailbox_obj.StorageGroupName in self.mdb_dn:                
                self.log.debug('found mailbox: {0}'.format(mailbox_obj.MailboxDisplayName))
                yield Mailbox(mailbox_obj)

    def mount(self):
        self.log.debug('mounting mailbox db: {0}'.format(self.mdb_dn))
        self.dispatched_obj.Mount()

    def dismount(self):
        self.log.debug('dismounting mailbox db: {0}'.format(self.mdb_dn))
        self.dispatched_obj.Dismount()

    def is_mounted(self):
        is_mounted = self.dispatched_obj.Status is 0
        if is_mounted:
            self.log.debug('mailbox db "{0}" is mounted'.format(self.mdb_dn))
        else:
            self.log.debug('mailbox db "{0}" is not mounted'.format(self.mdb_dn))
        return is_mounted

    def clean(self):
        raise NotImplemented('method is not implemented for exchange 2003')

    def remove(self):
        raise NotImplemented('method is not implemented for exchange 2003')

    def get_guid(self):
        raise NotImplemented('method is not implemented for exchange 2003')
    
    def delete_mailbox(self, name, domain):        
        if self.is_public_store:
            raise MailboxDatabaseError('object is not an mailbox database')        
	
        dn_domain = ",".join(['DC=' + dc for dc in domain.split('.')])
        person = Dispatch('CDO.Person')
        user_dn = 'LDAP://CN={0},CN=Users,{1}'.format(name, dn_domain)
        self.log.debug('open user: {0}'.format(user_dn))	
        person.DataSource.Open(user_dn, None, 3)                
        store = person.GetInterface('IMailboxStore')
	store.DeleteMailbox()
        person.DataSource.Save()                
	self.log.debug('delete mailbox: {0} in "{1}"'.format(name, self.mdb_dn))	

    def create_mailbox(self, name, password, domain):
        if self.is_public_store:
            raise MailboxDatabaseError('object is not an mailbox database')        
        dn_domain = ",".join(['DC=' + dc for dc in domain.split('.')])
        person = Dispatch('CDO.Person')
        user_dn = 'LDAP://CN={0},CN=Users,{1}'.format(name, dn_domain)
        self.log.debug('open user: {0}'.format(user_dn))	
        person.DataSource.Open(user_dn, None, 3)        
        
        store = person.GetInterface('IMailboxStore')
        store.CreateMailbox(self.mdb_dn)
        person.DataSource.Save()
        self.log.debug('create mailbox: {0} in "{1}"'.format(name, self.mdb_dn))	
        self.connect_to_mailbox_pop3(name, password)
        self.__open_exchange_wmi()
        mailbox_obj = self.wmi.ExecQuery('Select * from Exchange_Mailbox where MailboxDisplayName = "{0}"'.format(name))
        for obj in mailbox_obj:
            return Mailbox(obj, password)                

    def create_new_mailbox(self, name, password, domain):
        if self.is_public_store:
            raise MailboxDatabaseError('object is not an mailbox database')        
        dn_domain = ",".join(['DC=' + dc for dc in domain.split('.')])
        person = Dispatch('CDO.Person')
        person.FirstName = name
        person.Fields.Item('userPrincipalName').Value = name +'@' + domain
        person.Fields.Item('userAccountControl').Value = 0x10200
        person.Fields.Item('UserPassword').Value = password
        person.Fields.Update()
        user_dn = 'LDAP://CN={0},CN=Users,{1}'.format(name, dn_domain)
        self.log.debug('create new user: {0}'.format(user_dn))	
        person.DataSource.SaveTo(user_dn)

        store = person.GetInterface('IMailboxStore')
        store.CreateMailbox(self.mdb_dn)
        person.DataSource.Save()
        self.log.debug('create new mailbox: {0} in "{1}"'.format(name, self.mdb_dn))	
        self.connect_to_mailbox_pop3(name, password)
        self.__open_exchange_wmi()
        mailbox_obj = self.wmi.ExecQuery('Select * from Exchange_Mailbox where MailboxDisplayName = "{0}"'.format(name))
        for obj in mailbox_obj:
            return Mailbox(obj, password)

    def __open_exchange_wmi(self):
        self.wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\MicrosoftExchangeV2")

class Mailbox(Exchange2003Util):

    def __init__(self, wmi_obj, password = None):
        self.wmi_obj = wmi_obj
        self.password = password
        self.name = wmi_obj.MailboxDisplayName
        self.dn = wmi_obj.DN
        Exchange2003Util.__init__(self)

    def connect(self):
        self.wmi_obj.Reconnect(self.name)
        self.log.debug('connecting mailbox "{0}"'.format(self.name))

    def is_connected(self):
        raise NotImplemented('method is not implemented for exchange 2003')

    def check(self, domain_name = None):
        self.connect_to_mailbox_pop3(self.name, self.password)
        self.log.debug('mailbox "{0}" is checked'.format(self.name))
        
    def purge(self):
        self.wmi_obj.Purge()
        self.log.debug('mailbox "{0}" is purged'.format(self.name))